package com.smart.expand.config;


import com.smart.expand.bean.AnnotationMapping;
import com.smart.expand.bean.Constants;
import com.smart.expand.proxy.AnnotationProcessor;
import com.smart.expand.proxy.cglib.CglibDynamicProxy;
import com.smart.expand.proxy.jdk.JDKDynamicProxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.HashSet;
import java.util.Set;

/**
 * @author jiangjun
 */
public class BeanRunnerConfiguration implements ApplicationRunner {

    private static final Logger log = LoggerFactory.getLogger(BeanRunnerConfiguration.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        Set<AnnotationProcessor<? extends Annotation>> set = new HashSet<>();
        for (Class<AnnotationProcessor<? extends Annotation>> annotation : Constants.annotations) {
            set.add(applicationContext.getBean(annotation));
        }
        JDKDynamicProxy jdkDynamicProxy = JDKDynamicProxy.getInstance();
        jdkDynamicProxy.setAnnotationProcessors(set);
        CglibDynamicProxy cglibDynamicProxy = CglibDynamicProxy.getInstance();
        cglibDynamicProxy.setAnnotationProcessors(set);

        for (AnnotationMapping annotationMapping : Constants.annotationMappings) {
            Class aClass = annotationMapping.getAClass();
            Object bean = applicationContext.getBean(aClass);
            if (annotationMapping.getClassAnnotations().size() > 0) {
                annotationMapping.getClassAnnotations().forEach(processorClass -> {
                    AnnotationProcessor<? extends Annotation> annotationProcessor = applicationContext.getBean(processorClass);
                    Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ((ParameterizedType) processorClass.getGenericInterfaces()[0]).getActualTypeArguments()[0];
                    annotationProcessor.onClass((Class) annotationClass, bean);
                });
            }
            if (annotationMapping.getFieldAnnotations().size() > 0) {
                annotationMapping.getFieldAnnotations().forEach((field, annotationProcessorSet) -> {
                    field.setAccessible(true);
                    annotationProcessorSet.forEach(processorClass -> {
                        AnnotationProcessor<? extends Annotation> annotationProcessor = applicationContext.getBean(processorClass);
                        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ((ParameterizedType) processorClass.getGenericInterfaces()[0]).getActualTypeArguments()[0];
                        try {
                            Object result = annotationProcessor.onField((Class) annotationClass, bean, field, field.get(bean));
                            if (result != null) {
                                field.set(bean, result);
                            }
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    });
                });
            }
        }
    }

}
